home *** CD-ROM | disk | FTP | other *** search
/ Delphi Magazine Collection 2001 / Delphi Magazine Collection 20001 (2001).iso / DISKS / ISSUE13 / SYSTEM / HELPINFO.PAS next >
Encoding:
Pascal/Delphi Source File  |  1996-08-03  |  19.0 KB  |  590 lines

  1. unit HelpInfo;
  2.  
  3. {
  4.     Probs:
  5.  
  6.     = The GenDate stuff is seconds since 0:0:0 1/1/70
  7.     
  8.     }
  9.  
  10. interface
  11.  
  12. uses WinTypes, WinProcs, SysUtils;
  13.  
  14. const
  15.     HBadOpen = 1000;           { Can't open the help file   }
  16.     HIOError = 1001;           { I/O Error reading the file }
  17.     HBadSig  = 1002;           { Not a Windows Help file    }
  18.     HNoSys   = 1003;           { |SYSTEM file is missing !  }
  19.  
  20. function  OpenHelpFile (fileName: PChar; var hFile: Pointer): Integer;
  21. function  GetHelpFileName (hFile: Pointer): PChar;
  22. function  GetHelpFileSize (hFile: Pointer): LongInt;
  23. function  GetHelpFileDate (hFile: Pointer): PChar;
  24. function  GetHelpFileVersion (hFile: Pointer): Word;
  25. function  GetHelpFileCopyright (hFile: Pointer): PChar;
  26. function  GetHelpFileTitle (hFile: Pointer): PChar;
  27. function  GetHelpFileMacroCount (hFile: Pointer): Integer;
  28. function  GetHelpFileMacro (hFile: Pointer; idx: Integer): PChar;
  29. function  GetHelpFileFileCount (hFile: Pointer): Integer;
  30. function  GetHelpFileFileName (hFile: Pointer; idx: Integer): PChar;
  31. function  GetHelpFileFileSize (hFile: Pointer; idx: Integer): LongInt;
  32. {function  GetHelpFileGenDate (hFile: Pointer): PChar;}
  33. function  GetHelpFileCompression (hFile: Pointer): PChar;
  34. procedure CloseHelpFile (hFile: Pointer);
  35.  
  36. implementation
  37.  
  38. type
  39.     { Header for entire .HLP file }
  40.     THFileHeader = record
  41.                       MagicNumber: LongInt;
  42.                       WHIFSOffset: LongInt;
  43.                       Negative: LongInt;
  44.                       FileSize: LongInt;
  45.                   end;
  46.  
  47.     { Header for each internal WHIFS file }
  48.     TFileHeader = record
  49.               FilePlusHdr: LongInt;        { size with header }
  50.               FileSize:    LongInt;        { size without header }
  51.               NullByte: Byte;            { always zero... }
  52.           end;
  53.  
  54.     TWHIFSHeader = record
  55.                       Magic: array [0..17] of Byte;
  56.                       Junk: array [0..12] of Byte;
  57.                       Zero, NSplits, RootPage, MinusOne: Integer;
  58.                       TotPages, NLevels: Integer;
  59.                       TotalWHIFSEntries: LongInt;
  60.                    end;
  61.  
  62.     PSysHeader = ^TSysHeader;
  63.     TSysHeader = record
  64.                      Magic, Version, Revision, Zero: Byte;
  65.                      AlwaysOne: Word;
  66.                      GenDate: LongInt;
  67.                      Flags: Word;
  68.                  end;
  69.  
  70.     PHelpHandle = ^THelpHandle;
  71.     THelpHandle = record
  72.                       _fd: Integer;                       { handle of open help file     }
  73.                       _fn: String;                        { full pathname of the file    }
  74.                       _scratch1: array [0..255] of Char;  { scratch buffer             }
  75.                       _WHIFSStart: LongInt;               { start of file system         }
  76.               _FirstLeaf: Integer;          { first leaf node number       }
  77.                       _Title: array [0..255] of Char;     { help system title            }
  78.                       _Copyright: array [0..255] of Char; { help system copyright        }
  79.                       _MacroCount: Integer;               { number of startup macros     }
  80.                       _MacroData: PChar;                  { pointer to macro data        }
  81.                       _MacroDataSize: Word;               { size of _MacroData pointer   }
  82.                       _FileCount: Integer;                { # of files in help system    }
  83.                       _System: PSysHeader;                { pointer to in-memory |SYSTEM }
  84.                       _FileDirectory: Pointer;            { pointer to file directory    }
  85.                  end;
  86.  
  87. {------------------------------------------------------------------------------}
  88. {     Name:     GotoWHIFSPage                                                  }
  89. {     Purpose:  Internal routine - seek to a specified WHIFS page              }
  90. {------------------------------------------------------------------------------}
  91. procedure GotoWHIFSPage (p: PHelpHandle; pageNum: LongInt);
  92. begin
  93.     with p^ do _llseek (_fd, _WHIFSStart + (pageNum * 1024), 0);
  94. end;
  95.  
  96. {------------------------------------------------------------------------------}
  97. {     Name:     ReadString                                                     }
  98. {     Purpose:  Internal routine - just read a zero terminated string          }
  99. {------------------------------------------------------------------------------}
  100. procedure ReadString (fd: Integer; dest: PChar);
  101. begin
  102.     while True do
  103.     begin
  104.         _lread (fd, dest, 1);
  105.         if dest^ = #0 then break;
  106.         Inc (dest);
  107.     end;
  108. end;
  109.  
  110. {------------------------------------------------------------------------------}
  111. {     Name:     FindWHIFSFile                                                  }
  112. {     Purpose:  Internal routine - find a specified WHIFS file                 }
  113. {------------------------------------------------------------------------------}
  114. function FindWHIFSFile (p: PHelpHandle; fileName: PChar): LongInt;
  115. var
  116.     count: Integer;
  117.     fEntry: ^PChar;
  118. begin
  119.     FindWHIFSFile := -1;
  120.     with p^ do
  121.     begin
  122.         fEntry := _FileDirectory;
  123.         for count := 0 to _FileCount - 1 do
  124.         begin
  125.             if lstrcmp (fileName, fEntry^) = 0 then
  126.             begin
  127.                 FindWHIFSFile := PLongInt (fEntry^ + lstrlen (fEntry^) + 1)^;
  128.                 Exit;
  129.             end;
  130.             Inc (fEntry);
  131.         end;
  132.     end;
  133. end;
  134.  
  135. {------------------------------------------------------------------------------}
  136. {     Name:     ReadFileDirectory                                              }
  137. {     Purpose:  Internal routine - Read the file directory into memory.        }
  138. {------------------------------------------------------------------------------}
  139. procedure ReadFileDirectory (p: PHelpHandle);
  140. type
  141.     Node = record
  142.                Sig, Entries, Prev, Next: Integer;
  143.            end;
  144. var
  145.     f: Integer;
  146.     fEntry: ^PChar;
  147.     WHIFSNode: Node;
  148.     fname: array [0..255] of Char;
  149.  
  150. begin
  151.     with p^ do
  152.     begin
  153.         { Allocate memory for directory structure }
  154.  
  155.         _FileDirectory := AllocMem (_FileCount * sizeof (PChar));
  156.         fEntry := _FileDirectory;
  157.  
  158.         { Second pass - read the file directory }
  159.  
  160.         GotoWHIFSPage (p, _FirstLeaf);
  161.         while True do
  162.         begin
  163.             _lread (_fd, @WHIFSNode, sizeof (WHIFSNode));
  164.             for f := 1 to WHIFSNode.Entries do
  165.             begin
  166.                 ReadString (_fd, fname);
  167.                 fEntry^ := AllocMem (lstrlen (fname) + 1 + sizeof (LongInt));
  168.                 lstrcpy (fEntry^, fname);
  169.                 _lread (_fd, fEntry^ + lstrlen (fName) + 1, sizeof (LongInt));
  170.                 Inc (fEntry);
  171.             end;
  172.  
  173.             if WHIFSNode.Next = -1 then break;
  174.             GotoWHIFSPage (p, WHIFSNode.Next);
  175.         end;
  176.     end;
  177. end;
  178.  
  179. function LoadSystem (p: PHelpHandle): Integer;
  180. const
  181.     hpj_Title     = 1;                { title string }
  182.     hpj_Copyright = 2;                { copyright string }
  183.     hpj_Contents  = 3;                { contents }
  184.     hpj_MacroData = 4;
  185.     hpj_IconData  = 5;
  186.     hpj_SecWindow = 6;
  187.     hpj_Citation  = 8;
  188. var
  189.     Data: array [0..1] of Word;
  190.     DataPtr: PChar;
  191.  
  192.     bytesRead: LongInt;
  193.     sysOffset: LongInt;
  194.     fhdr: TFileHeader;
  195.     szBuffer: array [0..255] of Char;
  196.  
  197.     procedure AddMacro (macro: PChar);
  198.     var
  199.         sz: Word;
  200.     begin
  201.         with p^ do
  202.         begin
  203.             sz := lstrlen (macro) + 1;
  204.             if _MacroData = Nil then _MacroData := AllocMem (sz)
  205.             else _MacroData := ReAllocMem (_MacroData, _MacroDataSize, _MacroDataSize + sz);
  206.             lstrcpy (_MacroData + _MacroDataSize, macro);
  207.             Inc (_MacroDataSize, sz);
  208.             Inc (_MacroCount);
  209.         end;
  210.     end;
  211.  
  212. begin
  213.     LoadSystem := 0;
  214.     with p^ do
  215.     begin
  216.         sysOffset := FindWHIFSFile (p, '|SYSTEM');
  217.     if sysOffset = -1 then LoadSystem := HNoSys else
  218.      begin
  219.         { Read file header for |SYSTEM file }
  220.  
  221.         _llseek (_fd, sysOffset, 0);
  222.             _lread (_fd, @fhdr, sizeof (fhdr));
  223.             GetMem (_System, sizeof (TSysHeader));
  224.             _lread (_fd, PChar (_System), sizeof (TSysHeader));
  225.  
  226.             { Is it ancient ?  If so, title string only }
  227.  
  228.             if _System^.Revision = $F then
  229.             begin
  230.                 _lread (_fd, _Title, 33);
  231.                 Exit;
  232.             end;
  233.  
  234.             { Now, grab any stuff that follows system header }
  235.             { It's organised as <Type><Size><Data>,,,<Type><Size><Data> }
  236.  
  237.             bytesRead := sizeof (TSysHeader);
  238.             while fhdr.FileSize > bytesRead do
  239.             begin
  240.                 _lread (_fd, @Data, sizeof (Data));
  241.                 GetMem (DataPtr, Data [1]);
  242.                 _lread (_fd, DataPtr, Data [1]);
  243.  
  244.                 { Now case out on the data type }
  245.                 case Data [0] of
  246.                     hpj_Title:      lstrcpy (_Title, DataPtr);
  247.                     hpj_Copyright:  if DataPtr^ <> #0 then lstrcpy (_Copyright, DataPtr)
  248.                                     else lstrcpy (_Copyright, 'None');
  249.                     hpj_MacroData:  AddMacro (DataPtr);
  250.                 end;
  251.  
  252.                 FreeMem (DataPtr, Data [1]);
  253.                 Inc (bytesRead, Data [1] + sizeof (Data));
  254.             end;
  255.     end;
  256.     end;
  257. end;
  258.  
  259. function OpenHelpFile (fileName: PChar; var hFile: Pointer): Integer;
  260. var
  261.     aPage: Word;
  262.     junk: LongInt;
  263.     hdr: THFileHeader;
  264.     whdr: TWHIFSHeader;
  265.     f, fd, ret: Integer;
  266.     p: PHelpHandle absolute hFile;
  267.  
  268. begin
  269.     ret := 0;
  270.     hFile := Nil;
  271.     fd := _lopen (fileName, 0);
  272.     if fd = -1 then ret := HBadOpen else
  273.     if _lread (fd, @hdr, sizeof (hdr)) <> sizeof (hdr) then ret := HIOError else
  274.     if hdr.MagicNumber <> $00035F3F then ret := HBadSig;
  275.  
  276.     if ret = 0 then
  277.     begin
  278.         CloseHelpFile (hFile);
  279.         p := AllocMem (sizeof (THelpHandle));
  280.         p^._fd := fd;
  281.         p^._fn := StrPas (fileName);
  282.         lstrcpy (p^._Title, 'None');
  283.         lstrcpy (p^._Copyright, 'None');
  284.         p^._WHIFSStart := hdr.WHIFSOffset + sizeof (whdr);
  285.  
  286.         { Read the WHIFS header }
  287.  
  288.         _llseek (fd, hdr.WHIFSOffset, 0);
  289.         _lread (fd, @whdr, sizeof (whdr));
  290.         p^._FileCount := whdr.TotalWHIFSEntries;
  291.  
  292.     { and find root page }
  293.  
  294.         f := 1;
  295.         aPage := 0;
  296.         GotoWHIFSPage (p, whdr.RootPage);
  297.         while f < whdr.NLevels do
  298.         begin
  299.             _lread (fd, @junk, sizeof (junk));
  300.             _lread (fd, @aPage, sizeof (aPage));
  301.             GotoWHIFSPage (p, aPage);
  302.             Inc (f);
  303.         end;
  304.  
  305.         p^._FirstLeaf := aPage;
  306.  
  307.         { Read directory and load the |SYSTEM file }
  308.  
  309.         ReadFileDirectory (p);
  310.         ret := LoadSystem (p);
  311.  
  312.     end
  313.     else if fd <> -1 then _lclose (fd);
  314.  
  315.     OpenHelpFile := ret;
  316. end;
  317.  
  318. {------------------------------------------------------------------------------}
  319. {     Name:     GetHelpFileName                                                }
  320. {     Purpose:  Return full pathname of the current help file                  }
  321. {------------------------------------------------------------------------------}
  322. function GetHelpFileName (hFile: Pointer): PChar;
  323. var
  324.     p: PHelpHandle absolute hFile;
  325. begin
  326.     GetHelpFileName := '';
  327.     if hFile <> Nil then with p^ do
  328.     begin
  329.         StrPCopy (_scratch1, _fn);
  330.         GetHelpFileName := _scratch1;
  331.     end;
  332. end;
  333.  
  334. function GetHelpFileDate (hFile: Pointer): PChar;
  335. var
  336.     dt: TDateTime;
  337.     p: PHelpHandle absolute hFile;
  338. begin
  339.     GetHelpFileDate := '';
  340.     if hFile <> Nil then with p^ do
  341.     begin
  342.         dt := FileDateToDateTime (FileGetDate (_fd));
  343.         GetHelpFileDate := StrPCopy (_scratch1, FormatDateTime ('dddd, mmmm d, yyyy "at" hh:mm AM/PM', dt));
  344.     end;
  345. end;
  346.  
  347. function GetHelpFileVersion (hFile: Pointer): Word;
  348. var
  349.     p: PHelpHandle absolute hFile;
  350. begin
  351.     GetHelpFileVersion := $ffff;
  352.     if hFile <> Nil then with p^._System^ do
  353.         GetHelpFileVersion := (Revision shl 8) + Version;
  354. end;
  355.  
  356. {
  357.     This doesn't work because _GenDate is actually the number of seconds since
  358.     00:00:00 GMT Jan 1, 1970.  Bummer.....
  359.  
  360. function GetHelpFileGenDate (hFile: Pointer): PChar;
  361. var
  362.     dt: TDateTime;
  363.     p: PHelpHandle absolute hFile;
  364. begin
  365.     GetHelpFileGenDate := '';
  366.     if hFile <> Nil then with p^ do
  367.     begin
  368.         dt := FileDateToDateTime (_System^.GenDate);
  369.         GetHelpFileGenDate := StrPCopy (_scratch1, FormatDateTime ('dddd, mmmm d, yyyy "at" hh:mm AM/PM', dt));
  370.     end;
  371. end;   }
  372.  
  373. function GetHelpFileCompression (hFile: Pointer): PChar;
  374. var
  375.     Comp: Byte;
  376.     psz: PChar;
  377.     p: PHelpHandle absolute hFile;
  378. begin
  379.     GetHelpFileCompression := '';
  380.     if hFile <> Nil then with p^ do
  381.     begin
  382.         Comp := Lo (p^._System^.Flags);
  383.         if Comp = 0 then psz := 'None' else
  384.         if (Comp and $C) <> 0 then psz := 'Compressed' else
  385.         psz := 'Unknown';
  386.         GetHelpFileCompression := lstrcpy (_scratch1, psz);
  387.     end;
  388. end;
  389.  
  390. function GetHelpFileCopyright (hFile: Pointer): PChar;
  391. var
  392.     p: PHelpHandle absolute hFile;
  393. begin
  394.     GetHelpFileCopyright := '';
  395.     if hFile <> Nil then GetHelpFileCopyright := p^._Copyright;
  396. end;
  397.  
  398. function GetHelpFileTitle (hFile: Pointer): PChar;
  399. var
  400.     p: PHelpHandle absolute hFile;
  401. begin
  402.     GetHelpFileTitle := '';
  403.     if hFile <> Nil then GetHelpFileTitle := p^._Title;
  404. end;
  405.  
  406. function GetHelpFileSize (hFile: Pointer): LongInt;
  407. var
  408.     p: PHelpHandle absolute hFile;
  409. begin
  410.     GetHelpFileSize := 0;
  411.     if hFile <> Nil then GetHelpFileSize := _llseek (p^._fd, 0, 2);
  412. end;
  413.  
  414. function GetHelpFileMacroCount (hFile: Pointer): Integer;
  415. var
  416.     p: PHelpHandle absolute hFile;
  417. begin
  418.     GetHelpFileMacroCount := 0;
  419.     if hFile <> Nil then GetHelpFileMacroCount := p^._MacroCount;
  420. end;
  421.  
  422. function GetHelpFileFileCount (hFile: Pointer): Integer;
  423. var
  424.     p: PHelpHandle absolute hFile;
  425. begin
  426.     GetHelpFileFileCount := 0;
  427.     if hFile <> Nil then GetHelpFileFileCount := p^._FileCount;
  428. end;
  429.  
  430. function GetHelpFileMacro (hFile: Pointer; idx: Integer): PChar;
  431. var
  432.     psz: PChar;
  433.     p: PHelpHandle absolute hFile;
  434. begin
  435.     GetHelpFileMacro := '';
  436.     if (hFile <> Nil) and (idx < p^._MacroCount) then with p^ do
  437.     begin
  438.         psz := _MacroData;
  439.         while idx <> 0 do
  440.         begin
  441.             Inc (psz, lstrlen (psz) + 1);
  442.             Dec (idx);
  443.         end;
  444.  
  445.         GetHelpFileMacro := lstrcpy (_scratch1, psz);
  446.     end;
  447. end;
  448.  
  449. function GetHelpFileFileName (hFile: Pointer; idx: Integer): PChar;
  450. var
  451.     fEntry: ^PChar;
  452.     p: PHelpHandle absolute hFile;
  453. begin
  454.     GetHelpFileFileName := '';
  455.      if (hFile <> Nil) and (idx < p^._FileCount) then with p^ do
  456.      begin
  457.          fEntry := _FileDirectory;
  458.          Inc (fEntry, idx);
  459.          GetHelpFileFileName := lstrcpy (_scratch1, fEntry^);
  460.      end;
  461. end;
  462.  
  463. function GetHelpFileFileSize (hFile: Pointer; idx: Integer): LongInt;
  464. var
  465.     offset: LongInt;
  466.     fhdr: TFileHeader;
  467.     p: PHelpHandle absolute hFile;
  468. begin
  469.     GetHelpFileFileSize := 0;
  470.     if (hFile <> Nil) and (idx < p^._FileCount) then with p^ do
  471.     begin
  472.         offset := FindWHIFSFile (p, GetHelpFileFileName (p, idx));
  473.         { Read the file header in order to get the file size }
  474.         _llseek (_fd, offset, 0);
  475.         _lread (_fd, @fhdr, sizeof (fhdr));
  476.         GetHelpFileFileSize := fhdr.FileSize;
  477.     end;
  478. end;
  479.  
  480. procedure CloseHelpFile (hFile: Pointer);
  481. var
  482.     count: Integer;
  483.     fEntry: ^PChar;
  484.     p: PHelpHandle absolute hFile;
  485. begin
  486.     if hFile <> Nil then with p^ do
  487.     begin
  488.         _lclose (_fd);
  489.         if _System <> Nil then FreeMem (_System, sizeof (TSysHeader));
  490.         if _MacroDataSize <> 0 then FreeMem (_MacroData, _MacroDataSize);
  491.  
  492.         if _FileDirectory <> Nil then
  493.         begin
  494.             fEntry := _FileDirectory;
  495.             { First kill individual entries }
  496.             for count := 0 to _FileCount - 1 do
  497.             begin
  498.                 FreeMem (fEntry^, lstrlen (fEntry^) + 1 + sizeof (LongInt));
  499.                 Inc (fEntry);
  500.             end;
  501.             { Then kill directory itself }
  502.             FreeMem (_FileDirectory, _FileCount * sizeof (PChar));
  503.         end;
  504.         FreeMem (hFile, sizeof (THelpHandle));
  505.     end;
  506. end;
  507.  
  508. {-----------------------------------------------------------------------------------}
  509. {    Name:        Decompress                                                  }
  510. {    Purpose:    Decompress file data using Microsoft's "octet-based" LZ77   }
  511. {            sliding window algorithm.                                   }
  512. {    Parameters:    fd         ----   handle to the input file.                 }
  513. {            RawBytes   ----   number of bytes of input to process.      }
  514. {            Buffer     ----   destination buffer for decompressed data. }
  515. {-----------------------------------------------------------------------------------}
  516.  
  517. function Decompress (fd: Integer; RawBytes: Word; Buffer: PChar): Word;
  518. var
  519.     cc: Integer;                      { general dog's-body            }
  520.     win, wout: Word;                  { input/output byte counts      }
  521.     map: Byte;                        { the flag (or map) byte        }
  522.     octet: array [0..15] of Byte;     { bytes following map byte      }
  523.     octetSize: Integer;               { # of bytes specified by map   }
  524.     back: PChar;                      { backtrack pointer             }
  525.     count, index, len, dist: Integer; { stuff for backtracking        }
  526.  
  527. begin
  528.     win := 0;
  529.     wout := 0;
  530.  
  531.     { Repeat until read all bytes requested }
  532.  
  533.     while win < RawBytes do
  534.     begin
  535.         { Read map byte and figure out how many more bytes to read }
  536.  
  537.         octetSize := 8;
  538.         _lread (fd, @map, sizeof (map));
  539.         for cc := 0 to 7 do
  540.             if (map and (1 shl cc)) <> 0 then
  541.                 Inc (octetSize);
  542.  
  543.         { Ensure no overrun at end of compressed data }
  544.  
  545.         if RawBytes - win < octetSize then octetSize := RawBytes - win;
  546.  
  547.         _lread (fd, @octet, octetSize);
  548.         Inc (win, octetSize + 1);
  549.  
  550.         { Repeat for each of the eight bits of the flag byte }
  551.  
  552.         index := 0;
  553.         for count := 0 to 7 do
  554.         begin
  555.             if (map and (1 shl count)) = 0 then
  556.             begin
  557.                 { It's a literal - just copy it }
  558.  
  559.                 Buffer^ := Char (octet [index]);
  560.                 Inc (Buffer);
  561.                 Inc (index);
  562.                 inc (wout);
  563.             end
  564.             else
  565.             begin
  566.                 { It's a length/distance pair }
  567.  
  568.                len := ((octet [index + 1] and $F0) shr 4) + 3;
  569.                dist := (256 * (octet [index + 1] and 15)) +
  570.                               octet [index] + 1;
  571.                back := Buffer - dist;
  572.                Inc (wout, len);
  573.                while len <> 0 do
  574.                begin
  575.                    Dec (len);
  576.                    Buffer^ := back^;
  577.                    Inc (Buffer);
  578.                    Inc (back);
  579.                end;
  580.  
  581.                Inc (index, 2);
  582.             end
  583.         end;
  584.     end;
  585.  
  586.     Decompress := wout;
  587. end;
  588.  
  589. end.
  590.